
#ifndef Velyn_MeshRenderer_h
#define Velyn_MeshRenderer_h

#include "OpenGL.h"
#include "Types.h"
#include "TMesh.h"
// #include "VertexArray.h"
// #include "TriangleSet.h"

namespace Velyn {
    
    /*
     * MeshRender uses a singleton design pattern
     */

    class MeshRenderer {
        
    private: 
        
        bool showWireframe;
        bool showNormals;
        bool showVertices;
        
        VertexList* vertices;
        NormalList* normals;
        TextureList* textures;
        TriangleIndexList* triangles;

        static MeshRenderer* instance;
        
        MeshRenderer() {
            showWireframe = false;
            showNormals = false;
            showVertices = false;
        }
        
        void update(TriangleMesh* mesh) {
            vertices = mesh->getVertexList();
            normals = mesh->getNormalList();
            textures = mesh->getTextureList();
            triangles = mesh->getTriangleList();
            
            mesh->clean();
        }
        
        void displayNormals() {		
            for(int vertex = 0; vertex < vertices->size(); vertex++) {
                
                glColor3f(1.0f, 0.0f, 0.0f);
                glPushMatrix();
                glBegin(GL_LINES);
                
                glVertex3fv((*vertices)[vertex].getValue());
                glVertex3f((*vertices)[vertex].x + 0.1f * (*normals)[vertex].x,
                           (*vertices)[vertex].y + 0.1f * (*normals)[vertex].y,
                           (*vertices)[vertex].z + 0.1f * (*normals)[vertex].z);
                glEnd();
                glPopMatrix();
            }
        }
        
    public:
        
        ~MeshRenderer() {
            delete instance;
        }
        
        static MeshRenderer* getInstance() {
            if(instance == NULL) {
                instance = new MeshRenderer();
            }
            
            return instance;
        }
        
        void verticesToggle() {
            if(showVertices) {
                showVertices = false;
            } else {
                showVertices = true;
            }
        }
        
        void normalToggle() {
            if(showNormals) {
                showNormals = false;
            } else {
                showNormals = true;
            }
        }
        
        void wireframeToggle() {
            if(showWireframe) {
                showWireframe = false;
            } else {
                showWireframe = true;
            }
        }
    
        void render(TriangleMesh* mesh) {
            
            if(mesh->dirty()) {
                update(mesh);
            }
            
            if(showWireframe) {
                glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
            } else {
                glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            }
            
            if(showNormals) {
                displayNormals();
            }
            
            glEnableClientState(GL_VERTEX_ARRAY);
            glEnableClientState(GL_NORMAL_ARRAY);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
          
            glVertexPointer(3, GL_FLOAT, 0, &((*vertices)[0]));
            glNormalPointer(GL_FLOAT, 0, &((*normals)[0]));
            glTexCoordPointer(2, GL_FLOAT, 0, &((*textures)[0]));
            
            if(showVertices) {
                glDrawElements(GL_POINTS, (GLsizei)triangles->size(), GL_UNSIGNED_INT, &((*triangles)[0]));
            } else {
                glDrawElements(GL_TRIANGLES, (GLsizei)triangles->size(), GL_UNSIGNED_INT, &((*triangles)[0]));
            }
                
            glDisableClientState(GL_VERTEX_ARRAY);
            glDisableClientState(GL_NORMAL_ARRAY);
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        }
    };

    MeshRenderer* MeshRenderer::instance = NULL;
}

#endif
